package world

import (
	"MetroBuilderGameJam/gameEngine/drawer"
	"MetroBuilderGameJam/gameEngine/geom"
	"github.com/hajimehoshi/ebiten/v2"
	"math/rand/v2"
)

type Builder struct {
	availableField          geom.Size
	avoidSimilarNeighbour   bool
	minNeighbourDistance    float64
	PredefinedStations      []*Station
	StationTypesProbability map[StationType]float64
	offset                  geom.Vec2
}

func NewSimpleBuilder(center geom.Point) *Builder {
	return &Builder{
		availableField:        geom.Size{Width: 200, Height: 140},
		avoidSimilarNeighbour: true,
		minNeighbourDistance:  50,
		PredefinedStations: []*Station{
			//NewStation(StationTypeCross, center),
			//NewStationProject(StationTypeStar),
			//NewStationProject(StationTypePentagon),
			NewStationProject(StationTypeCircle),
			NewStationProject(StationTypeTriangle),
			NewStationProject(StationTypeSquare),
		},
		StationTypesProbability: map[StationType]float64{
			StationTypeCircle:   0.74,
			StationTypeTriangle: 0.20,
			StationTypeSquare:   0.03,
			StationTypePentagon: 0.01,
			StationTypeStar:     0.01,
			StationTypeCross:    0.01,
		},
		offset: center.AsVec2(),
	}
}

func (b *Builder) BuildNextStation(existsStations []*Station) *Station {
	if len(existsStations) < len(b.PredefinedStations) {
		station := b.PredefinedStations[len(existsStations)]
		if station.position == nil {
			pos := b.findSuitablePosition(station.shape, existsStations)
			station.position = &pos
		}
		return station
	}

	shape := b.randomStationShape()
	return NewStation(shape, b.findSuitablePosition(shape, existsStations))
}

func (b *Builder) findSuitablePosition(shape StationType, stations []*Station) geom.Point {
	tryingIndex := 0
	positions := make([]geom.Point, 0)
	sameTypePositions := make([]geom.Point, 0)
	for _, station := range stations {
		positions = append(positions, *station.position)
		if shape == station.shape {
			sameTypePositions = append(sameTypePositions, *station.position)
		}
	}
	for {
		pos := b.randomOffset().Add(b.offset).AsPoint()
		if tryingIndex > 100 {
			return pos
		}
		tryingIndex++
		if len(positions) > 0 {
			minNeighbourDistance := b.minNeighbourDistance + float64(100/len(positions))
			minDistance := pos.MinDistanceTo(positions)
			//fmt.Println("trying", tryingIndex, " minDistance for station at", pos, "is", minDistance)
			if minDistance < minNeighbourDistance {
				continue
			}
			if b.avoidSimilarNeighbour && len(sameTypePositions) > 0 {
				minDistance = pos.MinDistanceTo(sameTypePositions)
				if minDistance < b.minNeighbourDistance {
					continue
				}
			}
		}
		return pos
	}
}

func (b *Builder) randomOffset() geom.Vec2 {
	return geom.Vec2{
		X: 2 * (rand.Float64() - 0.5) * b.availableField.Width,
		Y: 2 * (rand.Float64() - 0.5) * b.availableField.Height,
	}
}

func (b *Builder) randomStationShape() StationType {
	var sum float64
	for _, v := range b.StationTypesProbability {
		sum += v
	}
	r := rand.Float64() * sum
	for k, v := range b.StationTypesProbability {
		if r < v {
			return k
		}
		r -= v
	}
	return StationType(0)
}

func (b *Builder) NewAvailableField(size geom.Size) {
	b.availableField = size
}

func (b *Builder) DebugAvailableRadius(screen *ebiten.Image) {
	d := drawer.NewCentralDrawer()
	d.Rectangle(screen, b.offset.AsPoint(), b.availableField, 1, 0, drawer.ColorGreen)
	d.Point(screen, b.offset.AsPoint(), 3, drawer.ColorRed)
	//fmt.Println("builder offset", b.offset)
}
